﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.Text.RegularExpressions;

namespace Xpus.Web.Common
{
	/// <summary>
	/// 表示应用程序配置。
	/// </summary>
	public class XpusApplication
	{
		/// <summary>
		/// 语言资源文件的路径。
		/// {0} - 应用程序的path属性。
		/// {1} - 语言名字。
		/// </summary>
		const string LanguagePath = @"~/App_Data/Languages/{0}.{1}.xml";

		/// <summary>
		/// 应用程序配置文件的路径。
		/// </summary>
		const string ApplicationConfigPath = @"~/App_Data/Applications.xml";

		/// <summary>
		/// 获取应用程序的二级域名。
		/// </summary>
		public string Domain { get; private set; }

		/// <summary>
		/// 获取应用程序对应的网站内部的路径。
		/// </summary>
		public string Path { get; private set; }

		/// <summary>
		/// 获取特属于当前应用程序的URL重写规则。
		/// </summary>
		public Dictionary<Regex, string> UrlRewritingRules { get; private set; }

		/// <summary>
		/// 直接支持的语言。
		/// </summary>
		List<string> SupportedCultures = new List<string>();

		/// <summary>
		/// 映射的语言。
		/// </summary>
		Dictionary<string, string> MappedCultures = new Dictionary<string, string>();

		/// <summary>
		/// 判断一种语言是否受支持。
		/// </summary>
		/// <param name="culture">语言名字。请使用全小写名字。</param>
		/// <returns>如果直接或间接支持该语言，返回true；否则返回false。</returns>
		public bool SupportCulture(string culture)
		{
			return SupportedCultures.Contains(culture) || MappedCultures.ContainsKey(culture);
		}

		/// <summary>
		/// 获取指定语言文件下的字符串资源。
		/// </summary>
		/// <param name="culture">语言名。请使用全小写名字。</param>
		/// <param name="key">字符串的键值。</param>
		/// <returns></returns>
		public string GetCultureString(string culture, string key)
		{
			if(!SupportCulture(culture))
				throw new XpusCultureNotSupportedException(culture);

			if(MappedCultures.ContainsKey(culture))
				culture = MappedCultures[culture];

			string path = string.Format(LanguagePath, this.Path, culture);
			XpusCulture cul = XpusCulture.LoadCulture(path);

			return cul.GetString(key);
		}

		/// <summary>
		/// 根据二级域名获取应用程序实例。
		/// </summary>
		/// <param name="domain">二级域名。请使用全小写域名。</param>
		/// <returns>具有指定二级域名的应用程序实例。如果不存在，返回null。</returns>
		public static XpusApplication GetByDomain(string domain)
		{
			if (domain == null)
				throw new ArgumentNullException("domain");

			XpusApplicationList apps = LoadApplicationConfig();
			if (!apps.ContainsKey(domain))
				return null;

			return apps[domain];
		}

		/// <summary>
		/// 从xml配置节点生成应用程序实例。
		/// </summary>
		/// <param name="node">带有应用程序配置的xml节点。</param>
		private XpusApplication(XmlNode node)
		{
			this.Domain = node.Attributes["domain"].Value;
			this.Path = node.Attributes["path"].Value;

			XmlNodeList supportedCultureNodes = node.SelectNodes("languages/supported/language");
			foreach (XmlNode n in supportedCultureNodes)
				this.SupportedCultures.Add(n.Attributes["name"].Value);

			XmlNodeList mappedCultureNodes = node.SelectNodes("languages/mapped/language");
			foreach (XmlNode n in mappedCultureNodes)
				this.MappedCultures.Add(n.Attributes["name"].Value, n.Attributes["target"].Value);

			XmlNodeList urlRewritingRuleNodes = node.SelectNodes("urlRewritingRules/rule");
			this.UrlRewritingRules = new Dictionary<Regex, string>();
			foreach (XmlNode n in urlRewritingRuleNodes)
				this.UrlRewritingRules.Add(
					new Regex(n.Attributes["source"].Value, RegexOptions.IgnoreCase),
					n.Attributes["target"].Value);
		}

		/// <summary>
		/// 加载所有应用程序配置。
		/// </summary>
		/// <returns>所有应用程序的列表。</returns>
		private static XpusApplicationList LoadApplicationConfig()
		{
			// 首先尝试从缓存中取得
			XpusApplicationList apps = XpusAppDataCache<XpusApplicationList>.Get(ApplicationConfigPath);
			if (apps != null)
				return apps;

			// 从文件中读取
			apps = new XpusApplicationList();
			string path = HttpContext.Current.Server.MapPath(ApplicationConfigPath);
			XmlDocument xdoc = new XmlDocument();
			xdoc.Load(path);
			XmlNodeList appnodes = xdoc.SelectNodes("/applications/application");
			foreach(XmlNode n in appnodes)
			{
				XpusApplication app = new XpusApplication(n);
				apps.Add(app.Domain, app);
			}

			// 放入缓存
			XpusAppDataCache<XpusApplicationList>.Set(ApplicationConfigPath, apps);
			return apps;
		}

		/// <summary>
		/// 表示xpus应用程序的列表。字典的键为应用程序的二级域名，字典的值为应用程序实例。
		/// </summary>
		class XpusApplicationList : Dictionary<string, XpusApplication>
		{
		}
	}
}
